using System;

using java.util;
using java.util.zip;
using java.io;
using java.util;

using System.IO;
using Icodeon.Util;


namespace Icodeon.Util
{
    public class ZipUtilsJSharp : IZipUtils
    {
        public void ZipFiles(
            IObserver observer,
            DirectoryInfo inputDirectory,
            FileInfo zipFile,
            string password,
            int compressionLevel)
        {

            try
            {
                //create a ZipOutputStream to zip the data to 
                ZipOutputStream zos = new
                ZipOutputStream(new FileOutputStream(zipFile.FullName));

                ZipDirectory(observer, inputDirectory.FullName, zos, "");
                //close the stream 
                zos.close();
            }
            catch (Exception e)
            {

            }
        }

        public void UnZipFiles(
            IObserver observer,
            FileInfo zipFile,
            DirectoryInfo outputDirectory,
            string password,
            bool deleteZipFile)
        {
            this.UnZipFile(observer, new java.io.File(zipFile.FullName), new java.io.File(outputDirectory.FullName));
        }

        private void ZipDirectory(IObserver observer, String dir2zip, ZipOutputStream zos, String folderPath)
        {

            //create a new File object based on the directory we have to zip File    
            java.io.File zipDir = new java.io.File(dir2zip);

            int progress = 10;
            if (observer != null)
            {
                observer.Callback(progress, String.Format("Zipping {0}", zipDir.getName()));
            }

            //get a listing of the directory content 
            String[] dirList = zipDir.list();
            sbyte[] readBuffer = new sbyte[2156];
            int bytesIn = 0;
            //loop through dirList, and zip the files 
            for (int i = 0; i < dirList.Length; i++)
            {
                java.io.File f = new java.io.File(zipDir, dirList[i]);
                if (f.isDirectory())
                {
                    //if the File object is a directory, call this 
                    //function again to add its content recursively 
                    String filePath = f.getPath();
                    if (string.IsNullOrEmpty(folderPath))
                    {
                        /*
                         * This is giving an empty directort at the root of the .zip file,
                         * so remove the emty string. See:
                         * 
                         * http://scorm2cc.codeplex.com/WorkItem/View.aspx?WorkItemId=10328
                         */
                        ZipDirectory(observer, filePath, zos, (f.getName()));
                    }
                    else
                    {
                        ZipDirectory(observer, filePath, zos, (folderPath + java.io.File.separator + f.getName()));
                    }
                    //loop again 
                    continue;
                }

                //if we reached here, the File object f was not a directory 
                //create a FileInputStream on top of f 
                FileInputStream fis = new FileInputStream(f);
                //create a new zip entry 
                ZipEntry anEntry = null;
                if (string.IsNullOrEmpty(folderPath))
                {
                    /*
                     * This is giving an empty directort at the root of the .zip file,
                     * so remove the emty string. See:
                     * 
                     * http://scorm2cc.codeplex.com/WorkItem/View.aspx?WorkItemId=10328
                     */
                    anEntry = new ZipEntry(f.getName());
                }
                else
                {
                    anEntry = new ZipEntry(folderPath + java.io.File.separator + f.getName());
                }
                //place the zip entry in the ZipOutputStream object 
                //System.Console.WriteLine("Adding the entry: " + folderPath + java.io.File.separator + f.getName());
                zos.putNextEntry(anEntry);
                //now write the content of the file to the ZipOutputStream 

                while ((bytesIn = fis.read(readBuffer)) != -1)
                {
                    zos.write(readBuffer, 0, bytesIn);
                    if (observer != null)
                    {
                        observer.Callback(progress,
                            String.Format("Adding to zip: {0}, {1} bytes", anEntry.getName(), bytesIn));
                    }
                }
                //close the Stream 
                fis.close();
            }

        }



        private void UnZipFile(IObserver observer, java.io.File zipFile, java.io.File targetFolder)
        {
            IOUtils ioUtils = new IOUtils();
            targetFolder.mkdirs();

            BufferedInputStream input = null;
            BufferedOutputStream output = null;
            ZipInputStream zIn = null;
            ZipEntry zipEntry;
            int bytesRead;
            int bufSize = 512;
            sbyte[] buf = new sbyte[bufSize];


            input = new BufferedInputStream(new FileInputStream(zipFile), bufSize);
            zIn = new ZipInputStream(input);

            int progress = 10;
            if (observer != null)
            {
                observer.Callback(progress, String.Format("Unzipping {0}", zipFile.getName()));
            }
            try
            {
                while ((zipEntry = zIn.getNextEntry()) != null)
                {
                    // Don't add directories
                    if (!zipEntry.isDirectory())
                    {
                        if (zipEntry.getName().Contains("tfs")) { continue; }
                        //java.io.File outFile = new java.io.File(targetFolder, zipEntry.getName());

                        /*
                        System.Console.WriteLine("Writing outfile=" + outFile.getCanonicalPath());
                        System.Console.WriteLine("Parent directory exists?=" + outFile.getParentFile().exists());
                        System.Console.WriteLine("Parent directory path=" + outFile.getParentFile().getCanonicalPath());
                        System.Console.WriteLine("Parent directory name=" + outFile.getParent());
                        */

                        String zipEntryName = zipEntry.getName();
                        //System.Console.WriteLine("zipEntry.getName()=" + zipEntry.getName());
                        String targetFolderPath = targetFolder.getCanonicalPath();
                        //System.Console.WriteLine("targetFolder.getCanonicalPath()=" + targetFolder.getCanonicalPath());

                        FileInfo combinedPath = ioUtils.CombineDirectoryNameAndFileName(new DirectoryInfo(targetFolderPath), zipEntryName);
                        //System.Console.WriteLine("combined path=" + combinedPath.FullName);
                        ioUtils.MakeDirectoriesInPath(combinedPath);
                        java.io.File outFile = new java.io.File(combinedPath.FullName);


                        /*
                        // Ensure that the parent Folder exists
                        if (!outFile.getParentFile().exists())
                        {
                            System.Console.WriteLine("Making directory path=" + outFile.getParentFile().getCanonicalPath());
                            outFile.getParentFile().mkdirs();
                        }
                        if (outFile.isDirectory())
                        {
                            if (!outFile.exists())
                            {
                                outFile.mkdirs();
                            }
                        }
                        */

                        output = new BufferedOutputStream(new FileOutputStream(outFile), bufSize);

                        int sleep_count = 0;

                        while ((bytesRead = zIn.read(buf)) != -1)
                        {
                            output.write(buf, 0, bytesRead);
                            if (observer != null)
                            {
                                observer.Callback(progress,
                                    String.Format("Extracting from zip: {0}, {1} bytes", zipEntryName, bytesRead));
                            }


                            // Allow other things to happen every 40 chunks
                            if (sleep_count >= 40)
                            {
                                try
                                {
                                    java.lang.Thread.sleep(1);
                                }
                                catch (java.lang.InterruptedException ex)
                                {
                                    ex.printStackTrace();
                                }
                                sleep_count = 0;
                            }

                            sleep_count++;

                        }

                        // Restore time stamp
                        outFile.setLastModified(zipEntry.getTime());

                        // Close File
                        output.flush();
                        output.close();
                    }

                    zIn.closeEntry();
                }

                zIn.close();

            }
            // We'll catch this exception to close the file otherwise it remains locked
            catch (java.io.IOException ex)
            {
                zIn.close();
                if (output != null)
                {
                    output.flush();
                    output.close();
                }
                // And throw it again
                throw ex;
            }
        }
    }
}
